package cz.worktracker.controller;

import cz.worktracker.enums.DataSetsEnum;
import cz.worktracker.model.entities.Taskinterval;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.ManagedBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
import org.hibernate.Criteria;
import org.hibernate.Query;

import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

/**
 * TaskintervalManager
 * Controller for working with JPA entity Taskinterval
 * @since 2010-04-23
 * @author kyjovsky
 */
@ManagedBean
@SessionScoped
@Named
public class TaskintervalManager extends AbstractManager
        implements Serializable {

    private static final long serialVersionUID = 1L;
    private Taskinterval taskinterval;

    /* TABULKA */
    private AbstractPaginator paginator;
    private DataModel dataModel;
    private DataSetsEnum DSEnum;
    private int rowsPerPage = DSEnum.ITEMS_PER_PAGE_DEFAULT.getValue();
    private String sortByField = DSEnum.SORT_BY_FIELD.getStringValue();
    private String sortDirection = DSEnum.SORT_DIRECTION.getStringValue();
    /* TABULKA */
    private ArrayList<Taskinterval> taskIntervalList;
    @Inject
    UserManager userManager;
    @Inject
    TaskManager taskManager;

    /**
     * Constructor
     */
    @PostConstruct
    public void construct() {
        init();
    }

    /**
     * Destructor
     */
    @PreDestroy
    public void destroy() {
        taskinterval = null;
        paginator = null;
        dataModel = null;
    }

    /**
     * Initialize values
     */
    private void init() {
        if (taskinterval == null) {
            taskIntervalList = new ArrayList<Taskinterval>();
            taskinterval = new Taskinterval();
            taskinterval.setIdTask(-1);
        }
    }

    /**
     * Get current interval
     * @return current Taskinterval
     */
    public Taskinterval getTaskinterval() {
        return taskinterval;
    }

    /**
     * Set current Taskinterval.
     * @param taskinterval interval
     */
    public void setTaskinterval(Taskinterval taskinterval) {
        this.taskinterval = taskinterval;
    }

    /**
     * Reset current dataModel.
     */
    private void resetDataModel() {
        dataModel = null;
    }

    /**
     * Reset current paginator.
     */
    public void resetPagination() {
        paginator = null;
    }

    /**
     * Returns page switcher and override abstract methods from AbstractPaginator.
     * @return Paginator object.
     */
    public AbstractPaginator getPaginator() {
        if (paginator == null) {
            paginator = new AbstractPaginator(rowsPerPage) {

                /*
                 * While creating a new instance we have to implement abstract
                 * methods defined in AbstractPaginator class.
                 */
                @Override
                public int getItemsCount() {
                    return getTaskintervalsCount();
                }

                @Override
                public DataModel createPageDataModel() {
                    ListDataModel list = null;

                    try {
                        Transaction tx = getSession().beginTransaction();
                        Criteria ct = getSession().createCriteria(Taskinterval.class);
                        ct.add(Restrictions.eq("idTask", taskManager.getTask().getId()));
                        /* TODO nefunguje :-( 
                        if( getSortDirection().equalsIgnoreCase( "asc" ) ) {
                        ct.addOrder( Order.asc( getSortByField()) );
                        } else {
                        ct.addOrder( Order.desc( getSortByField()) );
                        }
                         */

                        ct.addOrder(Order.desc("startTime"));
                        ct.setFirstResult(getPageFirstItem());
                        ct.setMaxResults(getPageSize());

                        list = new ListDataModel(ct.list());
                        tx.commit();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    return list;
                }
            };
        }
        return paginator;
    }

    /**
     * Returns count of records returned to view.
     * @return int
     */
    public int getRowsPerPage() {
        return rowsPerPage;
    }

    /**
     * Sets count of records returned to view.
     * @param rowsPerPage
     */
    public void setRowsPerPage(int rowsPerPage) {
        this.rowsPerPage = rowsPerPage;

        resetDataModel();
        resetPagination();
    }

    /**
     * Returns DataModel for dataTable.
     * @return DataModel for dataTable.
     */
    public DataModel getList() {
        if (dataModel == null) {
            dataModel = getPaginator().createPageDataModel();
        }

        return dataModel;
    }


    /**
     * Removes taskinterval records according to parent task id.
     * @param tId Id of parent Task.
     * @return boolean
     */
    public boolean removeByTaskId( int tId ) {
        Transaction tx = getSession().beginTransaction();
        String hql = "delete from Taskinterval as taskint where taskint.idTask = '" + tId + "'";
        Query q = getSession().createQuery( hql );
        q.executeUpdate();
        tx.commit();

        return true;
    }


    /**
     * Next page.
     */
    public void next() {
        getPaginator().nextPage();
        resetDataModel();
    }

    /**
     * Last page.
     */
    public void last() {
        getPaginator().lastPage();
        resetDataModel();
    }

    /**
     * Previous page.
     */
    public void previous() {
        getPaginator().previousPage();
        resetDataModel();
    }

    /**
     * First page.
     */
    public void first() {
        getPaginator().firstPage();
        resetDataModel();
    }

    /**
     * Increments count of items per page.
     */
    public void more() {
        setRowsPerPage(getRowsPerPage() + DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }

    /**
     * * Decrements count of items per page.
     */
    public void less() {
        setRowsPerPage(getRowsPerPage() - DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }

    /**
     * Retrieves sort params through context and initiates sort.
     */
    public void sort() {
        /**
         * Try to gain params send by <f:param />
         */
        FacesContext context = FacesContext.getCurrentInstance();
        Map requestMap = context.getExternalContext().getRequestParameterMap();
        setSortByField((String) requestMap.get("by"));
        setSortDirection((String) requestMap.get("order"));

        resetPagination();
        resetDataModel();

    }

    /**
     * Returns intervals count.
     * @return int means intervals count.
     */
    public int getTaskintervalsCount() {
        Transaction tx = getSession().beginTransaction();
        String hql = "select count(*) from Taskinterval as taskinterval where taskinterval.idTask = '" + taskManager.getTask().getId() + "'";
        int count = ((Long) (getSession().createQuery(hql).iterate().next())).intValue();
        tx.commit();
        return count;
    }

    /**
     * Returns field name in JPA entity according which sorting will be provided.
     * @return String
     */
    public String getSortByField() {
        return sortByField;
    }

    /**
     * Sets field name in JPA entity according which sorting will be provided.
     * @param sortByField
     */
    public void setSortByField(String sortByField) {
        this.sortByField = sortByField;
    }

    /**
     * Returns sort direction (ascending: asc; descending desc).
     * @return String
     */
    public String getSortDirection() {
        return sortDirection;
    }

    /**
     * Sets sort direction (ascending: asc; descending desc).
     * @param sortDirection
     */
    public void setSortDirection(String sortDirection) {
        this.sortDirection = sortDirection;
    }

    /**
     * Return total work time of current Task
     * Format: hh:mm:ss
     * @return String
     */
    public String getTotalTime() {

        int id_task = taskManager.getTask().getId();

        resetDataModel();
        ListDataModel list = null;

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("idTask", id_task));

            list = new ListDataModel(ct.list());
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Taskinterval tmp_interval;
        long diff = 0;

        for (int i = 0; i < list.getRowCount(); i++) {
            list.setRowIndex(i);
            tmp_interval = (Taskinterval) list.getRowData();
            if (tmp_interval.getStopTime() == null) {
                diff += (new Date()).getTime() - tmp_interval.getStartTime().getTime();
            } else {
                diff += tmp_interval.getStopTime().getTime() - tmp_interval.getStartTime().getTime();
            }

        }


        return getStringTime(new Date(diff - 3600000));


    }

    /**
     * Return formated time (hh:mm:ss).
     * @param date time which will be format
     * @return String
     */
    public String getStringTime(Date date) {
        String hours = ((Integer) date.getHours()).toString();
        String minutes;
        String seconds;

        if (date.getMinutes() < 10) {
            minutes = "0" + ((Integer) date.getMinutes()).toString();
        } else {
            minutes = ((Integer) date.getMinutes()).toString();
        }

        if (date.getSeconds() < 10) {
            seconds = "0" + ((Integer) date.getSeconds()).toString();
        } else {
            seconds = ((Integer) date.getSeconds()).toString();
        }

        return hours + ":" + minutes + ":" + seconds;
    }

    /**
     * Return length of current interval.
     * Format: hh:mm:ss or -null-
     * @return String
     */
    public String getIntervalLength() {

        int id_interval = ((Taskinterval) getList().getRowData()).getId();

        Taskinterval tmp_interval = new Taskinterval();


        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("id", id_interval));

            tmp_interval = (Taskinterval) ct.list().get(0);
            tx.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (tmp_interval.getId() == null) {
            return "- null -";
        }

        if (tmp_interval.getStopTime() == null) {
            return "> " + getStringTime(new Date((new Date().getTime()) - tmp_interval.getStartTime().getTime() - 3600000));
        } else {
            return getStringTime(new Date(tmp_interval.getStopTime().getTime() - tmp_interval.getStartTime().getTime() - 3600000));
        }

    }

    /**
     * Return time of last work at current task.
     * Format: day.month.year - hh:mm:ss
     * @return String if success, otherwise null
     */
    public String getLastWork() {
        Taskinterval newInterval;
        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("idTask", taskManager.getTask().getId()));
            ct.addOrder(Order.desc("stopTime"));
            int size = ct.list().size();
            if (size <= 0) {
                return null;
            }
            newInterval = (Taskinterval) ct.list().get(size - 1);
            if (newInterval.getStopTime() == null) {
                tx.commit();
                return getStringDate(new Date());
            }
            newInterval = (Taskinterval) ct.list().get(0);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        if (newInterval.getId() == null) {
            return "- null -";
        }

        return getStringDate(newInterval.getStopTime());
    }

    /**
     * Return formated date (day.month.year - hh:mm:ss).
     * @param date date which will be format
     * @return String
     */
    public String getStringDate(Date date) {
        String day = ((Integer) date.getDate()).toString();
        String month = ((Integer) (date.getMonth() + 1)).toString();
        String year = ((Integer) (date.getYear() + 1900)).toString();
        String hours;
        String minutes;
        if (date.getHours() < 10) {
            hours = "0" + ((Integer) date.getHours()).toString();
        } else {
            hours = ((Integer) date.getHours()).toString();
        }

        if (date.getMinutes() < 10) {
            minutes = "0" + ((Integer) date.getMinutes()).toString();
        } else {
            minutes = ((Integer) date.getMinutes()).toString();
        }

        return day + "." + month + "." + year + " - " + hours + ":" + minutes;
    }

    /**
     * Return time of first work at current task.
     * Format: day.month.year - hh:mm:ss
     * @return String if success, null otherwise
     */
    public String getFirstWork() {
        Taskinterval newInterval;
        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("idTask", taskManager.getTask().getId()));
            ct.addOrder(Order.asc("startTime"));
            if (ct.list().size() <= 0) {
                return null;
            }
            newInterval = (Taskinterval) ct.list().get(0);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        if (newInterval.getId() == null) {
            return "- null -";
        }

        return getStringDate(newInterval.getStartTime());
    }

    /**
     * Return started time of current interval
     * Format: day.month.year - hh:mm:ss or -null-
     * @return String
     */
    public String getStartTimeString() {

        int id_interval = ((Taskinterval) getList().getRowData()).getId();

        Taskinterval tmp_interval = new Taskinterval();

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("id", id_interval));
            tmp_interval = (Taskinterval) ct.list().get(0);
            tx.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (tmp_interval.getId() == null) {
            return "- null -";
        }

        return getStringDate(tmp_interval.getStartTime());

    }

    /**
     * Return stopped time of current interval
     * Format: day.month.year - hh:mm:ss or -null- or -RUN-
     * @return String of date, if interval is running -RUN-, if interval not found -null-
     */
    public String getStopTimeString() {

        int id_interval = ((Taskinterval) getList().getRowData()).getId();

        Taskinterval tmp_interval = new Taskinterval();

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("id", id_interval));

            tmp_interval = (Taskinterval) ct.list().get(0);
            tx.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (tmp_interval.getId() == null) {
            return "- null -";
        }

        if (tmp_interval.getStopTime() == null) {
            return "- RUN -";
        } else {
            return getStringDate(tmp_interval.getStopTime());
        }

    }

    /**
     * Remove current interval from db.
     */
    public void remove() {
        int id_interval = ((Taskinterval) getList().getRowData()).getId();

        Taskinterval tmp_interval = new Taskinterval();

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);
            ct.add(Restrictions.eq("id", id_interval));

            tmp_interval = (Taskinterval) ct.list().get(0);

            getSession().delete(tmp_interval);

            tx.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }

        resetDataModel();

    }

    /**
     * Returns recent interval.
     * @return ArrayList of Taskintervals
     */
    public ArrayList<Taskinterval> getRecentTasks() {
        ArrayList<Integer> taskIds = taskManager.getTaskIds();

        if (taskIntervalList == null) {
            taskIntervalList = new ArrayList<Taskinterval>();
        }

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Taskinterval.class);

            /*
            ct.setProjection( Projections.projectionList()
            .add( Projections.groupProperty("idTask") ) );*/
            ct.add(Restrictions.in("idTask", taskIds.toArray()));
            ct.addOrder(Order.desc("stopTime"));
            ct.addOrder(Order.desc("startTime"));

            ct.setMaxResults(DSEnum.ITEMS_MAX_RECENT_TIMERS.getValue());
            Iterator it = ct.list().iterator();

            getTaskIntervalList().clear();
            int i = 0;
            while (it.hasNext()) {
                // Taskinterval tmpTaskInt = (Taskinterval) it.next();
                getTaskIntervalList().add((Taskinterval) it.next());
                System.out.println(it.next());
            }

            tx.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return getTaskIntervalList();
    }

    /**
     * Returns current taskIntervalList
     * @return ArrayList of taskintervals
     */
    public ArrayList<Taskinterval> getTaskIntervalList() {
        return taskIntervalList;
    }

    /**
     * Sets intervals to taskIntervalList
     * @param ArrayList of taskintervals
     */
    public void setTaskIntervalList(ArrayList<Taskinterval> taskIntervalList) {
        this.taskIntervalList = taskIntervalList;
    }
}
